package com.gframework.sqlparam;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.lang.Nullable;

import com.gframework.util.ReflectUtils;


/**
 * SqlParam辅助操作工具类.
 * <pre>
 * 1、可以直接解析一个对象，根据其{@link ParamMode}注解来动态生成SqlParam对象。
 * </pre>
 * <p>不建议直接使用此类，因为这会让开发者关注的东西太多，本类方法已经兼容到{@link SqlParam}中，直接调用
 * {@link SqlParam#buildByParamModeAnd(Object)}
 * {@link SqlParam#buildByParamModeOr(Object)}
 * 方法即可。
 * 
 * @since 1.0.0
 * @author Ghwolf
 * @see ParamMode
 * @see SqlParam
 */
class SqlParamUtils {

	/**
	 * class类型解析结果存放集合
	 */
	private static Map<Class<?>, ParamModeHandler[]> beanParseCache = new ConcurrentHashMap<>();

	private SqlParamUtils() {
	}

	/**
	 * 用and连接符连接条件，解析对象属性中的{@link ParamMode}注解，并生成一个SqlParam类对象
	 * 
	 * @param obj 参数信息存放对象，只有包含了ParamMode注解的属性和方法才会被解析，可为null
	 * @return 返回SqlParam类对象，如果参数为null，则返回一个没有sql的SqlParam类对象
	 */
	public static SqlParam paramModeAnd(@Nullable Object obj) {
		SqlParam sp = SqlParam.and();
		return paramMode(sp, obj);
	}

	/**
	 * 用or连接符连接条件，解析对象属性中的{@link ParamMode}注解，并生成一个SqlParam类对象
	 * 
	 * @param obj 参数信息存放对象，只有包含了ParamMode注解的属性和方法才会被解析，可为null
	 * @return 返回SqlParam类对象，如果参数为null，则返回一个没有sql的SqlParam类对象
	 */
	public static SqlParam paramModeOr(@Nullable Object obj) {
		SqlParam sp = SqlParam.or();
		return paramMode(sp, obj);
	}

	private static SqlParam paramMode(SqlParam sp, Object obj) {
		if (obj == null) {
			return sp;
		}

		ParamModeHandler[] ms = beanParseCache.get(obj.getClass());
		if (ms == null) {
			ms = initBean(obj);
		}
		try {
			for (int x = 0; x < ms.length; x ++) {
				ms[x].doSet(sp, obj);
			}
		} catch (Exception e) {
			throw new ParamModeException("SqlParamUtils 执行方法时出错：" + ms, e);
		}

		return sp;
	}

	/**
	 * 初次操作一种类型时，解析其结构并将符合要求的getter方法返回.
	 * 解析过程如果出现不合法的配置，则会抛出异常。
	 * 
	 * @throws ParamModeConfigException 配置错误
	 */
	private static ParamModeHandler[] initBean(Object obj) {
		Class<?> cls = obj.getClass();
		ParamModeHandler[] handlers ;
		synchronized (cls) {
			if ((handlers = beanParseCache.get(cls)) != null) {
				return handlers;
			}
			List<Method> ms = ReflectUtils.findMethodsByAnno(obj, ParamMode.class);
			List<Field> fs = ReflectUtils.findFieldsByAnno(obj, ParamMode.class);
			handlers = new ParamModeHandler[ms.size() + fs.size()];
			int index = 0;
			
			
			for (Method m : ms) {
				ParamMode pm = m.getAnnotation(ParamMode.class);
				ParamModeHandler handler = createBean(pm.modeType().getParamModeHandlerClass(),pm,m);
				handlers[index ++] = handler ; 
			}
			
			for (Field f : fs) {
				ParamMode pm = f.getAnnotation(ParamMode.class);
				Method m = ReflectUtils.getFieldGetterMethod(f,true);
				if (m == null) {
					ParamModeHandler handler = createBean(pm.modeType().getParamModeHandlerClass(),pm,f);
					handlers[index ++] = handler ; 
				} else {
					ParamModeHandler handler = createBean(pm.modeType().getParamModeHandlerClass(),pm,m);
					handlers[index ++] = handler ; 
				}
			}
			beanParseCache.put(cls,handlers);
			
		}
		return handlers ;
	}
	
	
	private static ParamModeHandler createBean(Class<? extends ParamModeHandler> cls,ParamMode paramMode,Member member) {
		Constructor<? extends ParamModeHandler> con;
		try {
			con = cls.getDeclaredConstructor(ParamMode.class,Member.class);
		} catch (Exception e) {
			throw new ParamModeException("请保证提供一个包含有ParamMode和Method参数的构造方法在此类上：" + cls,e);
		}
		try {
			con.setAccessible(true);
			return con.newInstance(paramMode,member);
		} catch (Exception e) {
			throw new ParamModeException(cls + " 类实例化异常",e);
		}
		
	}

}
